#include "logic.h"

#include <QJsonDocument>
#include <QJsonObject>
#include <QFile>
#include <QCryptographicHash>
#include <QDir>
#include <QDirIterator>
#include <QFileInfo>
#include <QFileInfoList>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QNetworkReply>
#include <QDateTime>

#include <QUrl>
#include <QEventLoop>
#include <QDebug>


QJsonObject readJsonFile(QString name,QString defualt){
    QFile file(name); // JSON文件路径

    QByteArray data = QByteArray(defualt.toUtf8());
    if (!file.open(QIODevice::ReadOnly)) {
        // 文件打开失败处理
        qDebug() << "open fail" <<name;
        file.close();
        // return R"({"list":[],index:0})";
    }else{
        data = file.readAll();
        file.close();
        if(data.isEmpty() || data == ""){
            // data = R"({"list":[],index:0})";
            data = QByteArray(defualt.toUtf8());
        }
    }

    QJsonParseError error;
    auto jsonDoc = QJsonDocument::fromJson(data,&error);
    if (error.error == QJsonParseError::NoError) {
        if(jsonDoc.isObject()){
            // jsonObj = std::make_shared<QJsonObject>(jsonDoc.object());
            // jsonObj = new QJsonObject(jsonDoc.object());
            return jsonDoc.object();
        }else{
            qDebug() << "读取配置失败" <<jsonDoc.isArray();
        }
    }else{
        qDebug() << "JSON解析错误：" << error.errorString() << data;
    }

    return QJsonObject();
}

void saveJsonFile(QJsonObject &obj,QString name){
    qDebug() << "保存文件" +name;
    QFile file(name);
    file.open(QIODevice::WriteOnly);
    QJsonDocument jsonDoc(obj);
    auto wdata = jsonDoc.toJson(QJsonDocument::JsonFormat::Compact);
    file.write(wdata);
    file.close();
}


bool ensureDir(const QString &path) {
    QDir dir;
    QFileInfo pathInfo(path);
    //qDebug() << "ensureDir:" << path;
    // 如果路径已经存在，则直接返回成功
    if (pathInfo.exists() && pathInfo.isDir()) {
        //qDebug() << "exist Dir:" << path;
        return true;
    }

    // 获取路径的父目录部分
    QString parentPath = pathInfo.absolutePath();
    if (!dir.exists(parentPath)) {
        // 如果父目录不存在，则递归创建父目录
        if (!ensureDir(parentPath)) {
            return false; // 创建父目录失败
        }
    }

    // 创建当前目录
    if (!dir.mkdir(path)) {
        return false; // 创建当前目录失败
    }

    return true; // 成功创建目录
}



bool copyFileOverwrite(const QString &sourcePath, const QString &destinationPath) {
    // 检查源文件是否存在
    qDebug() <<"cp:" << sourcePath << " to:" << destinationPath;
    QFileInfo sourceInfo(sourcePath);
    if (!sourceInfo.exists() || !sourceInfo.isFile()) {
        qDebug() << "Source file does not exist or is not a file.";
        return false;
    }

    // 删除目标文件（如果存在）
    QFile destFile(destinationPath);
    if (destFile.exists()) {
        if (!destFile.remove()) {
            qDebug() << "Failed to remove the destination file.";
            return false;
        }
    }

    // 拷贝文件
    if (!QFile::copy(sourcePath, destinationPath)) {
        qDebug() << "Failed to copy the file.";
        return false;
    }

    return true;
}


bool deleteDirectory(const QString &dirPath) {
    QDir dir(dirPath);

    if (dir.exists(dirPath)) {
        QFileInfoList entries = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden  | QDir::AllEntries | QDir::Dirs | QDir::Files, QDir::DirsFirst);
        for (int i = 0; i < entries.size(); ++i) {
            if (entries[i].isDir()) {
                deleteDirectory(entries[i].absoluteFilePath()); // 递归删除子目录
            } else {
                QFile file(entries[i].absoluteFilePath());
                file.setPermissions(file.permissions() & ~QFile::WriteUser); // 尝试移除写权限
                if (!file.remove()) { // 删除文件
                    qDebug() << "Failed to delete file:" << entries[i].absoluteFilePath();
                    return false;
                }
            }
        }
        // 删除空目录本身
        if (!dir.rmdir(dirPath)) {
            qDebug() << "Failed to delete directory:" << dirPath;
            return false;
        }
    } else {
        qDebug() << "Directory does not exist:" << dirPath;
    }
    return true;
}

QString request(QString urlstr) {
    QNetworkAccessManager manager;
    QUrl url(urlstr);
    QNetworkRequest request(url);
    QNetworkReply *reply = manager.get(request);
    QEventLoop loop;
    QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
    loop.exec(); // 阻塞，直到请求完成
    QString result = "";
    if (reply->error() == QNetworkReply::NoError) {
        result = reply->readAll();
    } else {
        qDebug() << "Error:" << reply->errorString();
    }
    reply->deleteLater(); // 记得删除reply对象，以避免内存泄漏。
    return result;
}

Logic::Logic() {
    init();
}

Logic::~Logic() {
    // delete jsonObj;
}


Logic* Logic::_inst = nullptr;

Logic* Logic::getInstance(){
    if(_inst == nullptr){
        Logic::_inst = new Logic();
    }
    return Logic::_inst;
}


void Logic::init(){
    itemList = readJsonFile("./hopmeta.json","{\"list\":[],\"index\":0}");
    ensureDir("./picked/");
    ensureDir("./bak/");
}

int Logic::getCurIndex(){
    // return itemList->value("index").toInt();
    return itemList.value("index").toInt();
}

void Logic::setCurIndex(int idx){
    itemList["index"] = idx;
}

void Logic::append(QJsonObject &item){
    // auto list = itemList->value("list").toArray();
    // QJsonObject *obj = itemList.get();
    // (*obj)["index"] = 0;// QJsonValue(0);
    // (*itemList.get())["index"] = list.size()-1;

    auto list = itemList.value("list").toArray();

    itemList["index"] = list.size();

    list.append(item);
    itemList["list"] = list;

    saveJsonFile(itemList,"hopmeta.json");
}

void Logic::updateItem(QJsonObject &item){
    int idx = getCurIndex();
    auto list = itemList.value("list").toArray();
    list[idx] = item;
    itemList["list"] = list;
    saveJsonFile(itemList,"./hopmeta.json");
}

void Logic::remove(){
    int idx = getCurIndex();
    auto list = itemList.value("list").toArray();
    if(list.size() > idx){
        list.removeAt(idx);
    }
    itemList["list"] = list;
    setCurIndex(idx == 0? list.size()-1 : idx-1);
    saveJsonFile(itemList,"hopmeta.json");
}

bool Logic::isHave(QString name){
    auto list = itemList.value("list").toArray();

    for (const QJsonValue &value : list) {
        if(value.toObject().value("name") == name){
            return true;
        }
    }
    return false;
}

QJsonArray Logic::getItems(){
    return itemList.value("list").toArray();
}

QJsonObject Logic::curItem(){
    auto list = itemList.value("list").toArray();
    if(list.size() > 0){
        return list[getCurIndex()].toObject();
    }else{
        return QJsonObject();
    }
}

QString Logic::readFileMd5(QString filePath)
{
    QFile file(filePath);
    if(file.open(QIODevice::ReadOnly)){
        //这里 QCryptographicHash::Md5 是个枚举类型，可以说改成你需要的比如Sha1、Sha3
        QByteArray ba = QCryptographicHash::hash(file.readAll(), QCryptographicHash::Md5);
        // qDebug() << ba.toHex(); //打印MD5码，或改成你的返回
        file.close();
        return ba.toHex();
    }else{
        qDebug() << "打开文件失败:" + filePath;
    }
    return "";
}

QJsonObject* Logic::getCurMd5info(){
    refreshMd5info(nullptr);
    return &oldMd5Info;
}

QString Logic::refreshMd5info(ProgressCallback progress){
    if(progress == nullptr){//设置空函数
        progress = [](QString text,int type){};
    }
    auto item = curItem();
    // 获取目录列表，不包括隐藏文件


    QDir rootdir = QDir(item.value("dir").toString());
    if(!rootdir.exists()){
        progress("目录不存在",0);

        return "error";
    }

    progress("正在比较文件...",0);

    QString rootdirab = rootdir.absolutePath();

    QString distdir = QDir("./picked/"+ item.value("name").toString()).absolutePath();
    ensureDir(distdir);//存放文件目标路径

    QString verdir = QDir("./versions/").absolutePath();
    ensureDir(verdir);//存放md5配置的目录
    QString md5fileName = verdir + "/" + item.value("name").toString() + ".json";

    if(item.value("url").toString().length() > 5){
        QString data = request(item.value("url").toString());
        isExistVersion = data.length() > 0;
        if(isExistVersion){
            QJsonParseError error;
            auto jsonDoc = QJsonDocument::fromJson(QByteArray(data.toUtf8()),&error);
            if (error.error == QJsonParseError::NoError) {
                if(jsonDoc.isObject()){
                    // 使用线上的版本，直接删除当前目录
                    oldMd5Info = jsonDoc.object();
                    // 删除目标文件（如果存在）
                    if(!deleteDirectory(distdir)){
                        progress("任务停止，目标路径自动删除失败，请手动删除后继续；路径为：" + distdir,1);
                        return "error";
                    }
                }else{
                    progress("任务终止：网络版本文件，数据错误，非JSON对象",1);
                    return "error";
                }
            }else{
                progress("任务终止：网络版本文件，JSON解析错误" + error.errorString(),1);
                return "error";
            }
        }else{
            progress("任务终止：获取不到网络版本文件数据",1);
            return "error";
        }
    }else{
        oldMd5Info = readJsonFile(md5fileName,"{}");
        isExistVersion = QFile::exists(md5fileName);
    }
    return md5fileName;
}

void Logic::walk(ProgressCallback progress){
    qDebug() << "开始比较";
    auto item = curItem();
    // 获取目录列表，不包括隐藏文件
    QStringList filters ;
    if(item.value("filter") != ""){
        filters = item.value("filter").toString().split(",");
    }


    QDir rootdir = QDir(item.value("dir").toString());
    if(!rootdir.exists()){
        if(progress != nullptr)
            progress("目录不存在",0);
        return;
    }

    progress("正在比较文件...",0);

    QString rootdirab = rootdir.absolutePath();

    QString distdir = QDir("./picked/"+ item.value("name").toString()).absolutePath();
    ensureDir(distdir);//存放文件目标路径

    QString md5fileName = refreshMd5info(progress);

    if(!isExistVersion){
        progress("第一次（或版本信息文件丢失），将只生成当前版本信息",1);
    }
    qDebug() << "遍历开始";
    QJsonObject md5info = QJsonObject();
    if(traverseDirectoryWithIterator(rootdirab,item.value("dir").toString(),filters,md5info,progress)){
        QDateTime dateTime = QDateTime::currentDateTime(); // 获取当前日期和时间
        QString formattedDateTime = dateTime.toString("yyyy-MM-dd HH:mm:ss"); // 格式化日期时间
        md5info.insert("datetime",formattedDateTime);//记录日期
        if(oldMd5Info.contains("serial")){//记录序号
            md5info.insert("serial",oldMd5Info.value("serial").toInt() +1);
        }else{
            md5info.insert("serial",1);
        }

        saveJsonFile(md5info,md5fileName);

        copyFileOverwrite(md5fileName,distdir +"/hotpick.json");
        this->distDir = distdir;
        progress("\n任务已完成，存放路径:\n\n" + distdir,2);
    }else{
        progress("\n任务失败 版本文件不更新" ,1);
    }

    progress("执行完毕",0);
}

bool Logic::traverseDirectoryWithIterator(QString &rootdir,const QString &path,QStringList &filters,QJsonObject &result,ProgressCallback progress) {
    QDirIterator it(path, filters,QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);//已经包含子目录
    auto obj = curItem();
    filters.clear();
    while (it.hasNext()) {
        it.next();
        if (!it.fileInfo().isDir()) { // 如果是文件
            QString key = QString(it.filePath()).replace(rootdir,"");
            QString md5 = readFileMd5(it.filePath());
            if(isExistVersion){//存在文件才需要比对，首次为整个目录，只需要提取md5
                if(!oldMd5Info.contains(key) || oldMd5Info.value(key).toString() != md5){
                    QString filepath = QDir("./picked/" + obj.value("name").toString() + "/"  +QString(key).left(QString(key).lastIndexOf(it.fileName()))).absolutePath();
                    ensureDir(filepath +'/');
                    if(!copyFileOverwrite(it.filePath(),filepath + '/' + it.fileName())){
                        progress(key+":" + " copy failed！",1);
                        return false;
                    }else{
                        progress(key+" >> picked",1);
                    }
                }
            }

            progress(key,0);
            if(result.contains(key)){
                qDebug() << "err:duplicate key" <<key << " file:"<< it.filePath();
                return false;
            }else{
                result.insert(key,md5);
            }
        } else { // 如果是目录（递归调用）
            // qDebug()<< "递归: " << " fatherpath:"<< path << "\nfp:" <<it.filePath();
            // if(!traverseDirectoryWithIterator(rootdir,it.filePath(),filters,result,progress)){// 递归调用
            //     return false;
            // }
        }
    }
    return true;
}

QString Logic::getDistDir(){
    return this->distDir;
}

QString Logic::bakDistDir(){
    QFileInfo dist(distDir);
    //qDebug() << "ensureDir:" << path;
    if (dist.exists()) {
        if(distDir.length() > 0){
            QString bak = QString(distDir);
            bak.replace("/picked/","/bak/");
            qDebug()<<distDir << " bak:" <<bak;
            QFileInfo pathInfo(bak);
            //qDebug() << "ensureDir:" << path;
            if (pathInfo.exists()) {
                deleteDirectory(bak);
                qDebug()<<"删除老的目录";
            }
            QDir().rename(distDir,bak);
            return "\n文件夹已保存至备份目录" + bak +"\n注意：文件夹只会保留最后一份，删除更老的版本将无法找回";
        }
    }else{
        return "文件夹不存在";
    }
}
